import enum, math
from typing import Iterable, Union
from .trip import Trip

class VehStatus(enum.IntEnum):
    '''
    车辆状态枚举
        driving: 行驶中(前往目的地或充电站)
        pending: 已经通知SUMO发车, 但是SUMO中该车尚未出发
        charging: 正在充电站中
        parking: 正在停放（处于两次行程之间，或还未开始行程，或行程已结束）
        depleted: 电量耗尽
    '''
    driving = 0 
    pending = 1
    charging = 2
    parking = 3
    depleted = 4

class EV:
    '''电动汽车类'''
    def __init__(self, id:str, trips:Iterable[Trip], eta_c:float, eta_d:float,
                bcap:float, soc:float, consump:float, efc_rate:float, esc_rate:float, max_v2g_rate:float,
                omega:float, krel:float, kfc:float, ksc:float, kv2g:float):
        self._id = id                   # 车辆ID
        self._sta = VehStatus.parking   # 车辆状态
        self._cs = None                 # 目标充电站
        self._cost = 0                  # 该车辆的总充电成本 CNY
        self._earn = 0                  # 该车辆的总放电收益 CNY

        # 静态电池参数
        self._bcap = bcap               # 电池容量 kWh
        assert 0.<=soc<=1.
        self._elec = soc * bcap         # 当前电量 kWh
        self._consumption = consump     # 单位距离耗电量(kWh/米)
        self._efc_rate = efc_rate       # 期望的快充功率 kWh/s
        self._esc_rate = esc_rate       # 期望的慢充功率 kWh/s
        self._v2g_rate = max_v2g_rate   # 最大反向送电功率 kWh/s
        self._eta_charge = eta_c        # 充电效率
        self._eta_discharge = eta_d     # 放电效率

        # 动态电池参数
        self._rate = 0                  # 实际充电功率 kWh/s
        self._chtar = bcap              # 快充时，充电到多少kWh才会离开

        # 行程参数
        self._dis = 0                   # 已行驶距离 m
        self._trips = tuple(trips)      # 车辆行程列表
        self._trip_index = 0            # 当前行程编号(下标)

        # 用户决策参数
        self._w = omega                 # 决策参数
        assert 1<=krel<=2
        self._krel = krel               # 裕度系数
        assert 0<kfc<1
        self._kfc = kfc                 # 用户选择快充的SOC
        assert kfc<=ksc<1
        self._ksc = ksc                 # 用户选择慢充的SOC
        assert ksc<kv2g
        self._kv2g = kv2g               # 用户愿意加入V2G的SOC

    @property
    def estimated_charge_time(self)->float:
        '''在当前电量、充电目标和充电速率下，完成充电所需时间'''
        assert self._rate is not None
        if self._rate>0:
            return max((self._chtar-self._elec)/self._rate,0)
        else:
            return math.inf
    
    @property
    def elec(self)->float:
        '''电池现有电量 kWh'''
        return self._elec
    
    @property
    def SOC(self)->float:
        '''电池荷电状态(电量百分比)'''
        return self._elec/self._bcap
    
    @property
    def omega(self)->float:
        '''决策参数，用于选择快充站'''
        return self._w
    @omega.setter
    def omega(self,val:float): self._w=val

    @property
    def krel(self)->float:
        '''裕度系数'''
        return self._krel
    @krel.setter
    def krel(self,val:float):
        assert 1.<=val<=2.
        self._krel=val

    @property
    def kfc(self)->float:
        '''选择快充的SOC阈值'''
        return self._kfc
    @kfc.setter
    def kfc(self,val:float):
        assert 0.<=val<=self._ksc
        self._kfc=val

    @property
    def ksc(self)->float:
        '''选择慢充的SOC阈值'''
        return self._ksc
    @ksc.setter
    def ksc(self,val:float):
        assert self._kfc<=val<=1.
        self._ksc=val
    
    @property
    def kv2g(self)->float:
        '''选择慢充的SOC阈值'''
        return self._kv2g
    @kv2g.setter
    def kv2g(self,val:float):
        assert self._ksc<val
        self._kv2g=val

    @property
    def charge_target(self)->float:
        '''充电目标, 即在快充站充到此电量离开(kWh)'''
        return self._chtar
    @charge_target.setter
    def charge_target(self,val):
        assert 0<=val<=self._bcap
        self._chtar = val

    @property
    def eta_charge(self)->float:
        '''车辆充电效率'''
        return self._eta_charge
    
    @property
    def eta_discharge(self)->float:
        '''车辆放电效率'''
        return self._eta_discharge
    
    @property
    def rate(self)->float:
        '''车辆希望的充电速率, kWh/s'''
        return self._rate
    
    @property
    def max_v2g_rate(self)->float:
        '''车辆最大V2G反向送电速率, kWh/s'''
        return self._v2g_rate
    
    def start_charging(self,fast_charge=True):
        '''开始充电: 设置充电速率'''
        self._rate=self._efc_rate if fast_charge else self._esc_rate
    
    def stop_charging(self):
        '''停止充电: 设置充电速率为0'''
        self._rate=0

    @property
    def status(self)->VehStatus:
        '''车辆当前状态'''
        return self._sta
    @status.setter
    def status(self,val): self._sta=val

    @property
    def target_CS(self)->Union[None,str]:
        '''目标快充站的名称, 当此项为None时, 表明车辆未寻找充电站''' 
        return self._cs
    @target_CS.setter
    def target_CS(self,val): self._cs=val
    
    @property
    def ID(self)->str:
        '''车辆的字符串ID'''
        return self._id

    @property
    def full_battery(self)->float:
        '''车辆的电池容量kWh'''
        return self._bcap

    @property
    def battery(self)->float:
        '''车辆的电池电量kWh'''
        return self._elec

    @property
    def consumption(self)->float:
        '''车辆的单位距离耗电量kWh/m'''
        return self._consumption

    @property
    def odometer(self)->float:
        '''车辆在本次行程中已行驶里程(m)，注意从充电站离开视为新的行程'''
        return self._dis

    def clear_odometer(self):
        '''在行程开始前，清空里程计'''
        self._dis=0

    def drive(self,new_dis:float):
        '''开车状态下, 更新电池SOC和里程计'''
        assert new_dis >= self._dis
        self._elec -= (new_dis - self._dis) * self._consumption
        self._dis=new_dis
    
    def charge(self, sec:int, unit_cost:float)->float:
        '''
        以当前充电速率对电池充电sec秒, 并返回实际充电量(kWh)(已考虑损耗)
        注意：当电量(soc)超过80%时, 充电功率会缩放为rate*(3.4-3*soc)
        '''
        _elec = self._elec
        real_rate = self._rate if self.SOC <= 0.8 else self.rate*(3.4-3*self.SOC)
        self._elec += real_rate * sec * self._eta_charge
        if self._elec > self._bcap: self._elec = self._bcap
        if self._elec < 0: self._elec = 0
        delta_elec = self._elec - _elec
        self._cost += delta_elec * unit_cost
        return delta_elec
    
    def discharge(self, k:float, sec:int, unit_earn:float)->float:
        '''
        以当前放电速率的k倍对电池充电sec秒, 并返回实际放电量kWh(已考虑损耗)
        '''
        #assert 0<=k<=1
        #if self.SOC <= self._kv2g: return 0
        _elec = self._elec
        self._elec -= self._v2g_rate * sec * k
        if self.SOC <= self._kv2g:
            self._elec = self._bcap * self._kv2g
        delta_elec = (_elec - self._elec) * self._eta_discharge
        assert delta_elec >=0
        self._earn += delta_elec * unit_earn
        return delta_elec

    @property
    def trips(self)->tuple[Trip,...]: return self._trips

    @property
    def trip(self)->Trip: return self._trips[self._trip_index] # 获取当前的行程

    @property
    def trip_id(self)->int: return self._trip_index # 获取当前行程的ID(从0开始计算)
    
    def next_trip(self)->int:
        '''
        使行程ID加1, 并返回行程ID. 如果已经是最后一个行程, 返回-1.
        '''
        if self._trip_index==len(self.trips)-1:
            return -1
        self._trip_index+=1
        return self._trip_index
    
    @property
    def max_mileage(self)->float:
        '''获取当前电量下的最大续航里程(m)'''
        return self._elec/self._consumption
    
    def is_batt_enough(self,dist:float)->bool:
        '''用户判断现有电量是否足以行驶dist距离'''
        return self.max_mileage>=self._krel*dist
    
    def brief(self):
        '''获取本车辆的简要描述'''
        return f"{self._id}(电量{self.SOC*100:.1f}%, 行程{self._trip_index})"
    
    def __str__(self):
        return f"EV[ID='{self._id}', Status={self._sta}, Dist={self._dis}m, SOC={self.SOC*100:.1f}%, Bcap={self._bcap}kWh, ChTar={self._chtar}kWh, TarCS={self._cs}, Consump={self._consumption}KWh/m]"